﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using l1cs.server.server.utils;
using MySql.Data.MySqlClient;
using System.Data;
using l1cs.server.model;
using l1cs.server.templates;
using l1cs.server.server.datatables;
using l1cs.server.model.Instance;
using l1cs.server.utils;

namespace l1cs.server.datatables
{
    class SpawnTable
    {
        private static Logger _log = Logger.getLogger( typeof( SpawnTable ).FullName );

        private static SpawnTable _instance;

        private Dictionary<int, L1Spawn> _spawntable = new Dictionary<int, L1Spawn>();

        private int _highestId;

        public static SpawnTable getInstance()
        {
            if ( _instance == null )
            {
                _instance = new SpawnTable();
            }
            return _instance;
        }

        private SpawnTable()
        {
            PerformanceTimer timer = new PerformanceTimer();
            _log.info( "spawning mob..." );

            int spawnCount = 0;
            L1Spawn spawnDat;
            L1Npc template1;
            String sqlstr = "SELECT * FROM spawnlist";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                foreach ( DataRow item in wrapper.GetDataTable( sqlstr ).Rows )
                {
                    if ( Config.ALT_HALLOWEENIVENT == false )
                    {
                        int npcid = SqlUtils.toInt( item["id"] );
                        if ( npcid >= 26656 && npcid <= 26734 )
                        {
                            continue;
                        }
                    }
                    int npcTemplateId = SqlUtils.toInt( item["npc_templateid"] );
                    template1 = NpcTable.getInstance().getTemplate( npcTemplateId );
                    int count;

                    if ( template1 == null )
                    {
                        _log.warning( "mob data for id:" + npcTemplateId + " missing in npc table" );
                        spawnDat = null;
                    }
                    else
                    {
                        if ( SqlUtils.toInt( item["count"] ) == 0 )
                        {
                            continue;
                        }
                        int mapid = SqlUtils.toShort( item["mapid"] );
                        double amount_rate = MapsTable.getInstance().getMonsterAmount( mapid );
                        count = calcCount( template1, SqlUtils.toInt( item["count"] ), amount_rate );
                        if ( count == 0 )
                        {
                            continue;
                        }

                        spawnDat = new L1Spawn( template1 );
                        spawnDat.setId( SqlUtils.toInt( item["id"] ) );
                        spawnDat.setAmount( count );
                        spawnDat.setGroupId( SqlUtils.toInt( item["group_id"] ) );
                        spawnDat.setLocX( SqlUtils.toInt( item["locx"] ) );
                        spawnDat.setLocY( SqlUtils.toInt( item["locy"] ) );
                        spawnDat.setRandomx( SqlUtils.toInt( item["randomx"] ) );
                        spawnDat.setRandomy( SqlUtils.toInt( item["randomy"] ) );
                        spawnDat.setLocX1( SqlUtils.toInt( item["locx1"] ) );
                        spawnDat.setLocY1( SqlUtils.toInt( item["locy1"] ) );
                        spawnDat.setLocX2( SqlUtils.toInt( item["locx2"] ) );
                        spawnDat.setLocY2( SqlUtils.toInt( item["locy2"] ) );
                        spawnDat.setHeading( SqlUtils.toInt( item["heading"] ) );
                        spawnDat.setMinRespawnDelay( SqlUtils.toInt( item["min_respawn_delay"] ) );
                        spawnDat.setMaxRespawnDelay( SqlUtils.toInt( item["max_respawn_delay"] ) );
                        spawnDat.setMapId( SqlUtils.toShort( item["mapid"] ) );
                        spawnDat.setRespawnScreen( Convert.ToBoolean( item["respawn_screen"] ) );
                        spawnDat.setMovementDistance( SqlUtils.toInt( item["movement_distance"] ) );
                        spawnDat.setRest( Convert.ToBoolean( item["rest"] ) );
                        spawnDat.setSpawnType( SqlUtils.toInt( item["near_spawn"] ) );
                        spawnDat.setTime( SpawnTimeTable.getInstance().get( spawnDat.getId() ) );

                        spawnDat.setName( template1.get_name() );

                        if ( count > 1 && spawnDat.getLocX1() == 0 )
                        {
                            // 複数かつ固定spawnの場合は、個体数 * 6 の範囲spawnに変える。
                            // ただし範囲が30を超えないようにする
                            int range = Math.Min( count * 6, 30 );
                            spawnDat.setLocX1( spawnDat.getLocX() - range );
                            spawnDat.setLocY1( spawnDat.getLocY() - range );
                            spawnDat.setLocX2( spawnDat.getLocX() + range );
                            spawnDat.setLocY2( spawnDat.getLocY() + range );
                        }

                        // start the spawning
                        spawnDat.init();
                        spawnCount += spawnDat.getAmount();
                    }

                    _spawntable[spawnDat.getId()] = spawnDat;
                    if ( spawnDat.getId() > _highestId )
                    {
                        _highestId = spawnDat.getId();
                    }
                }
            }
            _log.config( "モンスター配置リスト " + _spawntable.Count + "件ロード" );
            _log.info( "OK! " + timer.get() + " ms" );
            _log.fine( "総モンスター数 " + spawnCount + "匹" );
        }

        public L1Spawn getTemplate( int Id )
        {/*TTOK*/
            return _spawntable[Id];
        }

        public void addNewSpawn( L1Spawn spawn )
        {/*TTOK*/
            _highestId++;
            spawn.setId( _highestId );
            _spawntable[spawn.getId()] = spawn;
        }

        public static void storeSpawn( L1PcInstance pc, L1Npc npc )
        {/*TTOK*/
            int count = 1;
            int randomXY = 12;
            int minRespawnDelay = 60;
            int maxRespawnDelay = 120;
            String note = npc.get_name();

            String sqlstr = @"INSERT INTO spawnlist SET 
                                    location          = ?1,
                                    count             = ?2,
                                    npc_templateid    = ?3,
                                    group_id          = ?4,
                                    locx              = ?5,
                                    locy              = ?6,
                                    randomx           = ?7,
                                    randomy           = ?8,
                                    heading           = ?9,
                                    min_respawn_delay = ?10,
                                    max_respawn_delay = ?11,
                                    mapid             = ?12";
            using ( MySqlWrapper wrapper = new MySqlWrapper() )
            {
                MySqlCommand cmd = new MySqlCommand( sqlstr );
                cmd.Parameters.Add( "?1", MySqlDbType.String ).Value = note;
                cmd.Parameters.Add( "?2", MySqlDbType.Int32 ).Value = count;
                cmd.Parameters.Add( "?3", MySqlDbType.Int32 ).Value = npc.get_npcId();
                cmd.Parameters.Add( "?4", MySqlDbType.Int32 ).Value = 0;
                cmd.Parameters.Add( "?5", MySqlDbType.Int32 ).Value = pc.getX();
                cmd.Parameters.Add( "?6", MySqlDbType.Int32 ).Value = pc.getY();
                cmd.Parameters.Add( "?7", MySqlDbType.Int32 ).Value = randomXY;
                cmd.Parameters.Add( "?8", MySqlDbType.Int32 ).Value = randomXY;
                cmd.Parameters.Add( "?9", MySqlDbType.Int32 ).Value = pc.getHeading();
                cmd.Parameters.Add( "?10", MySqlDbType.Int32 ).Value = minRespawnDelay;
                cmd.Parameters.Add( "?11", MySqlDbType.Int32 ).Value = maxRespawnDelay;
                cmd.Parameters.Add( "?12", MySqlDbType.Int32 ).Value = pc.getMapId();
                wrapper.ExecNonQuery( cmd );
            }
        }

        private static int calcCount( L1Npc npc, int count, double rate )
        {/*TTOK*/
            if ( rate == 0 )
            {
                return 0;
            }
            if ( rate == 1 || npc.isAmountFixed() )
            {
                return count;
            }
            else
            {
                return NumberUtil.randomRound( ( count * rate ) );
            }
        }
    }
}
